/**
 * \file    Vector3.h
 * \brief   包含Vector3类。
 */
#ifndef RVO_VECTOR3_H_
#define RVO_VECTOR3_H_

#include "API.h"

#include <cmath>
#include <cstddef>
#include <ostream>

namespace RVO
{
	/**
	 * \brief  定义一个三维向量。
	 */
	class Vector3
	{
	public:
		/**
		 * \brief   构造并初始化一个三维向量实例为零。
		 */
		RVO_API inline Vector3()
		{
			val_[0] = 0.0f;
			val_[1] = 0.0f;
			val_[2] = 0.0f;
		}

		/**
		 * \brief   从指定的三维向量构造并初始化一个三维向量。
		 * \param   vector  包含xyz坐标的三维向量。
		 */
		RVO_API inline Vector3(const Vector3 &vector)
		{
			val_[0] = vector[0];
			val_[1] = vector[1];
			val_[2] = vector[2];
		}

		/**
		 * \brief   从指定的三元素数组构造并初始化一个三维向量。
		 * \param   val  包含xyz坐标的三元素数组。
		 */
		RVO_API inline explicit Vector3(const float val[3])
		{
			val_[0] = val[0];
			val_[1] = val[1];
			val_[2] = val[2];
		}

		/**
		 * \brief   从指定的xyz坐标构造并初始化一个三维向量。
		 * \param   x  三维向量的x坐标。
		 * \param   y  三维向量的y坐标。
		 * \param   z  三维向量的z坐标。
		 */
		RVO_API inline Vector3(float x, float y, float z)
		{
			val_[0] = x;
			val_[1] = y;
			val_[2] = z;
		}

		/**
		 * \brief   返回此三维向量的x坐标。
		 * \return  三维向量的x坐标。
		 */
		RVO_API inline float x() const { return val_[0]; }

		/**
		 * \brief   返回此三维向量的y坐标。
		 * \return  三维向量的y坐标。
		 */
		RVO_API inline float y() const { return val_[1]; }

		/**
		 * \brief   返回此三维向量的z坐标。
		 * \return  三维向量的z坐标。
		 */
		RVO_API inline float z() const { return val_[2]; }

		/**
		 * \brief   返回此三维向量的指定坐标。
		 * \param   i  要返回的坐标（0 <= i < 3）。
		 * \return  三维向量的指定坐标。
		 */
		RVO_API inline float operator[](size_t i) const { return val_[i]; }

		/**
		 * \brief   返回此三维向量的指定坐标的引用。
		 * \param   i  要返回引用的坐标（0 <= i < 3）。
		 * \return  三维向量的指定坐标的引用。
		 */
		RVO_API inline float &operator[](size_t i) { return val_[i]; }

		/**
		 * \brief   计算此三维向量的取反。
		 * \return  此三维向量的取反。
		 */
		RVO_API inline Vector3 operator-() const
		{
			return Vector3(-val_[0], -val_[1], -val_[2]);
		}

		/**
		 * \brief   计算此三维向量与指定三维向量的点积。
		 * \param   vector  用于计算点积的三维向量。
		 * \return  此三维向量与指定三维向量的点积。
		 */
		RVO_API inline float operator*(const Vector3 &vector) const
		{
			return val_[0] * vector[0] + val_[1] * vector[1] + val_[2] * vector[2];
		}

		/**
		 * \brief   计算此三维向量与指定标量值的标量乘法。
		 * \param   scalar  用于计算标量乘法的标量值。
		 * \return  此三维向量与指定标量值的标量乘法。
		 */
		RVO_API inline Vector3 operator*(float scalar) const
		{
			return Vector3(val_[0] * scalar, val_[1] * scalar, val_[2] * scalar);
		}

		/**
		 * \brief   计算此三维向量与指定标量值的标量除法。
		 * \param   scalar  用于计算标量除法的标量值。
		 * \return  此三维向量与指定标量值的标量除法。
		 */
		RVO_API inline Vector3 operator/(float scalar) const
		{
			const float invScalar = 1.0f / scalar;

			return Vector3(val_[0] * invScalar, val_[1] * invScalar, val_[2] * invScalar);
		}

		/**
		 * \brief   计算此三维向量与指定三维向量的向量和。
		 * \param   vector  用于计算向量和的三维向量。
		 * \return  此三维向量与指定三维向量的向量和。
		 */
		RVO_API inline Vector3 operator+(const Vector3 &vector) const
		{
			return Vector3(val_[0] + vector[0], val_[1] + vector[1], val_[2] + vector[2]);
		}

		/**
		 * \brief   计算此三维向量与指定三维向量的向量差。
		 * \param   vector  用于计算向量差的三维向量。
		 * \return  此三维向量与指定三维向量的向量差。
		 */
		RVO_API inline Vector3 operator-(const Vector3 &vector) const
		{
			return Vector3(val_[0] - vector[0], val_[1] - vector[1], val_[2] - vector[2]);
		}

		/**
		 * \brief   测试此三维向量是否与指定三维向量相等。
		 * \param   vector  用于测试相等性的三维向量。
		 * \return  如果三维向量相等则为真。
		 */
		RVO_API inline bool operator==(const Vector3 &vector) const
		{
			return val_[0] == vector[0] && val_[1] == vector[1] && val_[2] == vector[2];
		}

		/**
		 * \brief   测试此三维向量是否与指定三维向量不相等。
		 * \param   vector  用于测试不相等性的三维向量。
		 * \return  如果三维向量不相等则为真。
		 */
		RVO_API inline bool operator!=(const Vector3 &vector) const
		{
			return val_[0] != vector[0] || val_[1] != vector[1] || val_[2] != vector[2];
		}

		/**
		 * \brief   设置此三维向量的值为自身与指定标量值的标量乘法。
		 * \param   scalar  用于计算标量乘法的标量值。
		 * \return  此三维向量的引用。
		 */
		RVO_API inline Vector3 &operator*=(float scalar)
		{
			val_[0] *= scalar;
			val_[1] *= scalar;
			val_[2] *= scalar;

			return *this;
		}

		/**
		 * \brief   设置此三维向量的值为自身与指定标量值的标量除法。
		 * \param   scalar  用于计算标量除法的标量值。
		 * \return  此三维向量的引用。
		 */
		RVO_API inline Vector3 &operator/=(float scalar)
		{
			const float invScalar = 1.0f / scalar;

			val_[0] *= invScalar;
			val_[1] *= invScalar;
			val_[2] *= invScalar;

			return *this;
		}

		/**
		 * \brief   设置此三维向量的值为自身与指定三维向量的向量和。
		 * \param   vector  用于计算向量和的三维向量。
		 * \return  此三维向量的引用。
		 */
		RVO_API inline Vector3 &operator+=(const Vector3 &vector)
		{
			val_[0] += vector[0];
			val_[1] += vector[1];
			val_[2] += vector[2];

			return *this;
		}

		/**
		 * \brief   设置此三维向量的值为自身与指定三维向量的向量差。
		 * \param   vector  用于计算向量差的三维向量。
		 * \return  此三维向量的引用。
		 */
		RVO_API inline Vector3 &operator-=(const Vector3 &vector)
		{
			val_[0] -= vector[0];
			val_[1] -= vector[1];
			val_[2] -= vector[2];

			return *this;
		}

	private:
		float val_[3];
	};

	/**
	 * \relates  Vector3
	 * \brief    计算指定三维向量与指定标量值的标量乘法。
	 * \param    scalar  用于计算标量乘法的标量值。
	 * \param    vector  用于计算标量乘法的三维向量。
	 * \return   三维向量与标量值的标量乘法。
	 */
	inline Vector3 operator*(float scalar, const Vector3 &vector)
	{
		return Vector3(scalar * vector[0], scalar * vector[1], scalar * vector[2]);
	}

	/**
	 * \relates  Vector3
	 * \brief    计算指定三维向量的叉积。
	 * \param    vector1  用于计算叉积的第一个向量。
	 * \param    vector2  用于计算叉积的第二个向量。
	 * \return   两个指定向量的叉积。
	 */
	inline Vector3 cross(const Vector3 &vector1, const Vector3 &vector2)
	{
		return Vector3(vector1[1] * vector2[2] - vector1[2] * vector2[1], vector1[2] * vector2[0] - vector1[0] * vector2[2], vector1[0] * vector2[1] - vector1[1] * vector2[0]);
	}

	/**
	 * \relates  Vector3
	 * \brief    将指定的三维向量插入指定的输出流。
	 * \param    os      要插入三维向量的输出流。
	 * \param    vector  要插入输出流的三维向量。
	 * \return   输出流的引用。
	 */
	inline std::ostream &operator<<(std::ostream &os, const Vector3 &vector)
	{
		os << "(" << vector[0] << "," << vector[1] << "," << vector[2] << ")";

		return os;
	}

	/**
	 * \relates  Vector3
	 * \brief    计算指定三维向量的长度。
	 * \param    vector  要计算长度的三维向量。
	 * \return   三维向量的长度。
	 */
	inline float abs(const Vector3 &vector)
	{
		return std::sqrt(vector * vector);
	}

	/**
	 * \relates  Vector3
	 * \brief    计算指定三维向量的平方长度。
	 * \param    vector  要计算平方长度的三维向量。
	 * \return   三维向量的平方长度。
	 */
	inline float absSq(const Vector3 &vector)
	{
		return vector * vector;
	}

	/**
	 * \relates  Vector3
	 * \brief    计算指定三维向量的归一化。
	 * \param    vector  要计算归一化的三维向量。
	 * \return   三维向量的归一化。
	 */
	inline Vector3 normalize(const Vector3 &vector)
	{
		return vector / abs(vector);
	}
}

#endif
